package puzzle.hashikake.solver;

import java.util.ArrayList;
import java.util.List;

import puzzle.common.data.HatanException;
import puzzle.common.solver.BoardRule;
import puzzle.hashikake.data.HashiState;
import puzzle.hashikake.data.HashikakeBoard;
import puzzle.hashikake.data.ShimaDef;

class NumberRule implements BoardRule<HashikakeBoard> {

	public boolean apply(HashikakeBoard b) throws HatanException {
		boolean isChanged = false;
		for (ShimaDef shima : b.getShimaList()) {
			isChanged |= applyShima(b, shima);
		}
		return isChanged;
	}

	private boolean applyShima(HashikakeBoard b, ShimaDef shima) throws HatanException {
		boolean isChanged = false;

		HashiState[] hashiStates = b.getHashiStatesForShima(shima);

		List<HashiState> unfixedHashiStates = new ArrayList<HashiState>(4);

		int remainings = shima.n;
		int possibilityFlag = 0;
		for (HashiState s : hashiStates) {
			if (!s.isFixed()) {
				unfixedHashiStates.add(s);
				possibilityFlag = (possibilityFlag << 4) | s.getPossibilityFlag();
			} else {
				remainings -= s.getNumber();
			}
		}
		
		if (remainings < 0) {
			throw new HatanException();
		}
		
		if (unfixedHashiStates.size() == 0) {
			return false;
		}

		List<Integer> combinations = CombinationDictionary.INSTANCE.getCombinations(
				unfixedHashiStates.size(), remainings);
		
		if (combinations == null) {
			throw new HatanException();
		}

		int coordinatedPossibilityFlag = 0;
		for (int c : combinations) {
			if ((possibilityFlag & c) == c) {
				coordinatedPossibilityFlag |= c;
			}
		}

		for (int i = unfixedHashiStates.size() - 1; i >= 0; i--) {
			HashiState s = unfixedHashiStates.get(i);
			isChanged |= s.setPossibilityFlag(coordinatedPossibilityFlag & 0x07);
			coordinatedPossibilityFlag >>= 4;
		}

//		List<Integer> possibleCombinations = new ArrayList<Integer>();
//		for (int c : combinations) {
//			if ((possibilityFlag & c) == c) {
//				possibleCombinations.add(c);
//			}
//		}
//		
//		for (int i = unfixedHashiStates.size() - 1; i >= 0; i--) {
//			HashiState s = unfixedHashiStates.get(i);
//			Set<Integer> localPossibleCombinationSet = new HashSet<Integer>();
//			for (int c : possibleCombinations) {
//				localPossibleCombinationSet.add(c & 0x07);
//			}
//			
//			if (localPossibleCombinationSet.size() == 1) {
//				int c = localPossibleCombinationSet.iterator().next();
//				isChanged |= s.setPossibilityFlag(c);
//			} else if (localPossibleCombinationSet.size() == 0) {
//				throw new HatanException();
//			}
//			
//		}

		return isChanged;
	}

}
